import secrets
import hashlib
from datetime import datetime, date, timedelta
from typing import List, Optional, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import HTTPException

from module_speech.dao.speech_client_dao import ClientUserDao
from module_speech.dao.speech_usage_dao import ClientUsageDao
from module_speech.entity.do.speech_client_user_do import SpeechClientUserDO
from module_speech.entity.vo.speech_client_vo import (
    ClientUserModel, ClientUserCreateModel, ClientUserUpdateModel,
    ClientUserQueryModel, ApiKeyModel, QuotaModel
)
from utils.log_util import logger
from utils.common_util import CamelCaseUtil
from exceptions.exception import ServiceException


class ClientUserService:
    """
    客户端用户服务层
    """
    
    # API密钥配置
    API_KEY_LENGTH = 32
    API_SECRET_LENGTH = 64
    
    # 默认配置
    DEFAULT_MONTHLY_QUOTA = 100  # 分钟
    DEFAULT_RATE_LIMIT = 10      # 每分钟请求数
    
    @classmethod
    async def create_client_user(cls, db: AsyncSession, user_data: ClientUserCreateModel) -> ClientUserModel:
        """
        创建客户端用户
        
        :param db: 数据库会话
        :param user_data: 用户创建数据
        :return: 创建的用户信息
        """
        try:
            # 检查用户名是否已存在
            existing_user = await ClientUserDao.get_client_user_by_username(db, user_data.username)
            if existing_user:
                raise ServiceException(message="用户名已存在")
            
            # 检查邮箱是否已存在
            if user_data.email:
                existing_email = await ClientUserDao.get_client_user_by_email(db, user_data.email)
                if existing_email:
                    raise ServiceException(message="邮箱已存在")
            
            # 生成API密钥
            api_key, api_secret = cls.generate_api_keys()
            
            # 创建用户对象
            client_user = SpeechClientUserDO(
                username=user_data.username,
                display_name=user_data.display_name,
                email=user_data.email,
                phone=user_data.phone,
                api_key=api_key,
                api_secret=api_secret,
                client_type=user_data.client_type or 1,
                plan_type=user_data.plan_type or 'free',
                quota_monthly=user_data.quota_monthly or cls.DEFAULT_MONTHLY_QUOTA,
                quota_used_monthly=0,
                quota_reset_date=date.today() + timedelta(days=30),
                rate_limit_per_minute=user_data.rate_limit_per_minute or cls.DEFAULT_RATE_LIMIT,
                allowed_models=user_data.allowed_models or ["sensevoice-small"],
                allowed_features=user_data.allowed_features or {"upload": True, "realtime": False, "batch": False},
                ip_whitelist=user_data.ip_whitelist,
                webhook_url=user_data.webhook_url,
                organization=user_data.organization,
                status=1,  # 启用
                is_verified=0,  # 未验证
                create_time=datetime.now(),
                update_time=datetime.now()
            )
            
            # 保存到数据库
            created_user = await ClientUserDao.add_client_user_dao(db, client_user)
            await db.commit()
            
            logger.info(f"创建客户端用户成功: {created_user.username} (ID: {created_user.client_user_id})")
            
            # 转换为响应模型
            return cls._convert_to_client_user_model(created_user)
            
        except Exception as e:
            await db.rollback()
            logger.error(f"创建客户端用户失败: {str(e)}")
            raise ServiceException(message=f"创建客户端用户失败: {str(e)}")
    
    @classmethod
    async def update_client_user(cls, db: AsyncSession, client_user_id: int, 
                                user_data: ClientUserUpdateModel) -> ClientUserModel:
        """
        更新客户端用户信息
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param user_data: 更新数据
        :return: 更新后的用户信息
        """
        try:
            # 获取现有用户
            client_user = await ClientUserDao.get_client_user_by_id(db, client_user_id)
            if not client_user:
                raise ServiceException(message="用户不存在")
            
            # 检查用户名冲突（如果要更新用户名）
            if user_data.username and user_data.username != client_user.username:
                existing_user = await ClientUserDao.get_client_user_by_username(db, user_data.username)
                if existing_user:
                    raise ServiceException(message="用户名已存在")
                client_user.username = user_data.username
            
            # 检查邮箱冲突（如果要更新邮箱）
            if user_data.email and user_data.email != client_user.email:
                existing_email = await ClientUserDao.get_client_user_by_email(db, user_data.email)
                if existing_email:
                    raise ServiceException(message="邮箱已存在")
                client_user.email = user_data.email
            
            # 更新其他字段
            if user_data.display_name is not None:
                client_user.display_name = user_data.display_name
            if user_data.phone is not None:
                client_user.phone = user_data.phone
            if user_data.avatar is not None:
                client_user.avatar = user_data.avatar
            if user_data.client_type is not None:
                client_user.client_type = user_data.client_type
            if user_data.plan_type is not None:
                client_user.plan_type = user_data.plan_type
            if user_data.quota_monthly is not None:
                client_user.quota_monthly = user_data.quota_monthly
            if user_data.rate_limit_per_minute is not None:
                client_user.rate_limit_per_minute = user_data.rate_limit_per_minute
            if user_data.allowed_models is not None:
                client_user.allowed_models = user_data.allowed_models
            if user_data.allowed_features is not None:
                client_user.allowed_features = user_data.allowed_features
            if user_data.ip_whitelist is not None:
                client_user.ip_whitelist = user_data.ip_whitelist
            if user_data.webhook_url is not None:
                client_user.webhook_url = user_data.webhook_url
            if user_data.organization is not None:
                client_user.organization = user_data.organization
            if user_data.status is not None:
                client_user.status = user_data.status
            if user_data.is_verified is not None:
                client_user.is_verified = user_data.is_verified
            
            # 保存更新
            updated_user = await ClientUserDao.update_client_user_dao(db, client_user)
            await db.commit()
            
            logger.info(f"更新客户端用户成功: {updated_user.username} (ID: {updated_user.client_user_id})")
            
            return cls._convert_to_client_user_model(updated_user)
            
        except Exception as e:
            await db.rollback()
            logger.error(f"更新客户端用户失败: {str(e)}")
            raise ServiceException(message=f"更新客户端用户失败: {str(e)}")
    
    @classmethod
    async def get_client_users(cls, db: AsyncSession, query_params: ClientUserQueryModel) -> Dict[str, Any]:
        """
        获取客户端用户列表
        
        :param db: 数据库会话
        :param query_params: 查询参数
        :return: 分页结果
        """
        try:
            # 获取用户列表
            users = await ClientUserDao.get_client_users_list(db, query_params)
            
            # 获取总数
            total = await ClientUserDao.count_client_users(db, query_params)
            
            # 转换为响应格式
            user_list = []
            for user in users:
                user_model = cls._convert_to_client_user_model(user)
                # 隐藏敏感信息
                user_model.api_secret = "***hidden***"
                user_list.append(user_model.dict())
            
            return {
                'rows': user_list,
                'total': total,
                'page_num': query_params.page_num,
                'page_size': query_params.page_size
            }
            
        except Exception as e:
            logger.error(f"获取客户端用户列表失败: {str(e)}")
            raise ServiceException(message=f"获取客户端用户列表失败: {str(e)}")
    
    @classmethod
    async def get_client_user_by_id(cls, db: AsyncSession, client_user_id: int, 
                                   include_sensitive: bool = False) -> ClientUserModel:
        """
        根据ID获取客户端用户详情
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param include_sensitive: 是否包含敏感信息
        :return: 用户详情
        """
        try:
            client_user = await ClientUserDao.get_client_user_by_id(db, client_user_id)
            if not client_user:
                raise ServiceException(message="用户不存在")
            
            user_model = cls._convert_to_client_user_model(client_user)
            
            # 是否隐藏敏感信息
            if not include_sensitive:
                user_model.api_secret = "***hidden***"
            
            return user_model
            
        except Exception as e:
            logger.error(f"获取客户端用户详情失败: {str(e)}")
            raise ServiceException(message=f"获取客户端用户详情失败: {str(e)}")
    
    @classmethod
    async def delete_client_user(cls, db: AsyncSession, client_user_id: int) -> bool:
        """
        删除客户端用户
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :return: 删除是否成功
        """
        try:
            # 检查用户是否存在
            client_user = await ClientUserDao.get_client_user_by_id(db, client_user_id)
            if not client_user:
                raise ServiceException(message="用户不存在")
            
            # 执行软删除
            result = await ClientUserDao.delete_client_user_dao(db, client_user_id)
            await db.commit()
            
            if result:
                logger.info(f"删除客户端用户成功: {client_user.username} (ID: {client_user_id})")
            
            return result
            
        except Exception as e:
            await db.rollback()
            logger.error(f"删除客户端用户失败: {str(e)}")
            raise ServiceException(message=f"删除客户端用户失败: {str(e)}")
    
    @classmethod
    async def reset_api_keys(cls, db: AsyncSession, client_user_id: int) -> ApiKeyModel:
        """
        重置API密钥
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :return: 新的API密钥信息
        """
        try:
            # 检查用户是否存在
            client_user = await ClientUserDao.get_client_user_by_id(db, client_user_id)
            if not client_user:
                raise ServiceException(message="用户不存在")
            
            # 生成新的API密钥
            new_api_key, new_api_secret = cls.generate_api_keys()
            
            # 更新数据库
            result = await ClientUserDao.update_api_keys(db, client_user_id, new_api_key, new_api_secret)
            await db.commit()
            
            if not result:
                raise ServiceException(message="更新API密钥失败")
            
            logger.info(f"重置API密钥成功: 用户ID {client_user_id}")
            
            return ApiKeyModel(
                api_key=new_api_key,
                api_secret=new_api_secret,
                reset_time=datetime.now()
            )
            
        except Exception as e:
            await db.rollback()
            logger.error(f"重置API密钥失败: {str(e)}")
            raise ServiceException(message=f"重置API密钥失败: {str(e)}")
    
    @classmethod
    async def update_quota_settings(cls, db: AsyncSession, client_user_id: int,
                                   quota_monthly: int, rate_limit_per_minute: int = None) -> bool:
        """
        更新配额设置
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :param quota_monthly: 月度配额（分钟）
        :param rate_limit_per_minute: 每分钟请求限制（可选）
        :return: 更新是否成功
        """
        try:
            # 获取用户
            client_user = await ClientUserDao.get_client_user_by_id(db, client_user_id)
            if not client_user:
                raise ServiceException(message="用户不存在")
            
            # 更新配额设置
            client_user.quota_monthly = quota_monthly
            if rate_limit_per_minute is not None:
                client_user.rate_limit_per_minute = rate_limit_per_minute
            
            # 保存更新
            await ClientUserDao.update_client_user_dao(db, client_user)
            await db.commit()
            
            logger.info(f"更新配额设置成功: 用户ID {client_user_id}, 配额: {quota_monthly}分钟")
            
            return True
            
        except Exception as e:
            await db.rollback()
            logger.error(f"更新配额设置失败: {str(e)}")
            raise ServiceException(message=f"更新配额设置失败: {str(e)}")
    
    @classmethod
    async def get_quota_usage(cls, db: AsyncSession, client_user_id: int) -> QuotaModel:
        """
        获取配额使用情况
        
        :param db: 数据库会话
        :param client_user_id: 客户端用户ID
        :return: 配额使用信息
        """
        try:
            # 获取用户
            client_user = await ClientUserDao.get_client_user_by_id(db, client_user_id)
            if not client_user:
                raise ServiceException(message="用户不存在")
            
            # 获取当月使用统计
            current_month_usage = await ClientUsageDao.get_monthly_usage(
                db, client_user_id, 
                date.today().year, 
                date.today().month
            )
            
            remaining_quota = max(0, client_user.quota_monthly - client_user.quota_used_monthly)
            usage_percentage = (client_user.quota_used_monthly / client_user.quota_monthly * 100) if client_user.quota_monthly > 0 else 0
            
            return QuotaModel(
                quota_monthly=client_user.quota_monthly,
                quota_used_monthly=client_user.quota_used_monthly,
                quota_remaining=remaining_quota,
                usage_percentage=round(usage_percentage, 2),
                quota_reset_date=client_user.quota_reset_date,
                rate_limit_per_minute=client_user.rate_limit_per_minute,
                current_month_stats=current_month_usage
            )
            
        except Exception as e:
            logger.error(f"获取配额使用情况失败: {str(e)}")
            raise ServiceException(message=f"获取配额使用情况失败: {str(e)}")
    
    @classmethod
    async def verify_ip_whitelist(cls, client_user: SpeechClientUserDO, client_ip: str) -> bool:
        """
        验证IP白名单
        
        :param client_user: 客户端用户对象
        :param client_ip: 客户端IP
        :return: 是否通过验证
        """
        if not client_user.ip_whitelist:
            return True  # 没有设置白名单，允许所有IP
        
        return client_ip in client_user.ip_whitelist
    
    @classmethod
    async def get_client_user_statistics(cls, db: AsyncSession) -> Dict[str, Any]:
        """
        获取客户端用户统计信息
        
        :param db: 数据库会话
        :return: 统计信息
        """
        try:
            stats = await ClientUserDao.get_client_user_statistics(db)
            return stats
            
        except Exception as e:
            logger.error(f"获取客户端用户统计失败: {str(e)}")
            raise ServiceException(message=f"获取客户端用户统计失败: {str(e)}")
    
    @classmethod
    def generate_api_keys(cls) -> tuple[str, str]:
        """
        生成API密钥对
        
        :return: (api_key, api_secret)
        """
        # 生成API Key（32位随机字符串）
        api_key = secrets.token_urlsafe(cls.API_KEY_LENGTH)[:cls.API_KEY_LENGTH]
        
        # 生成API Secret（64位随机字符串）
        api_secret = secrets.token_urlsafe(cls.API_SECRET_LENGTH)[:cls.API_SECRET_LENGTH]
        
        return api_key, api_secret
    
    @classmethod
    def _convert_to_client_user_model(cls, client_user: SpeechClientUserDO) -> ClientUserModel:
        """
        将DO对象转换为VO模型
        
        :param client_user: 客户端用户DO对象
        :return: 客户端用户VO模型
        """
        return ClientUserModel(
            client_user_id=client_user.client_user_id,
            username=client_user.username,
            display_name=client_user.display_name,
            email=client_user.email,
            phone=client_user.phone,
            avatar=client_user.avatar,
            api_key=client_user.api_key,
            api_secret=client_user.api_secret,
            client_type=client_user.client_type,
            plan_type=client_user.plan_type,
            quota_monthly=client_user.quota_monthly,
            quota_used_monthly=client_user.quota_used_monthly,
            quota_reset_date=client_user.quota_reset_date,
            rate_limit_per_minute=client_user.rate_limit_per_minute,
            allowed_models=client_user.allowed_models,
            allowed_features=client_user.allowed_features,
            ip_whitelist=client_user.ip_whitelist,
            webhook_url=client_user.webhook_url,
            organization=client_user.organization,
            status=client_user.status,
            is_verified=client_user.is_verified,
            last_login_time=client_user.last_login_time,
            last_login_ip=client_user.last_login_ip,
            create_time=client_user.create_time,
            update_time=client_user.update_time
        ) 